package cz.pecinovsky.česky.oopnz.správce;
//Příliš žluťoučký kůň úpěl ďábelské ódy


import static cz.pecinovsky.česky.oopnz.správce.Správce_Test.SprávcePlátna;
////////////////////////////////////////////////////////////////////////////////
//%%+  +++++ Konec ignorovaného počátečního textu +++++

//%A+ >=133x
import cz.pecinovsky.česky.oopnz.utility.Barva;
//%A-
//%A+ >135
import cz.pecinovsky.česky.oopnz.utility.Hodiny;
import cz.pecinovsky.česky.oopnz.utility.IČasovaný;
//%A-
//%A+ >=133x
import cz.pecinovsky.česky.oopnz.utility.IO;
import cz.pecinovsky.česky.oopnz.utility.Pozice;
import cz.pecinovsky.česky.oopnz.utility.Rozměr;
//%A-

import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Point;
import java.awt.Toolkit;

import java.awt.event.KeyListener;
import java.awt.event.MouseListener;
import java.awt.image.BufferedImage;

import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;

import java.util.ArrayList;
//%A+ >135
import java.util.Collection;
//%A-
import java.util.Collections;
import java.util.List;
import java.util.ListIterator;
import java.util.Properties;

import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;



//%X+ xxxxx Začátek přeskakovaného textu xxxxx
/*******************************************************************************
 *<pre>
 * Předchozí:   Není - toto_je_nově_definovaná_třída
 *              Ttt v projektu Ppp
 * Následující: Prozatím není
 *
 * Projekt  Ppp
 *   + Přidáno
 *   - Odebráno
 *   ~ Změněno
 *</pre>
 *
 * @author Rudolf PECINOVSKÝ
 * @version 6.03.2017 — 2010-11-08
 */
public class Správce_Test
{    private Správce_Test() {}   static
////////////////////////////////////////////////////////////////////////////////
//%X- ----- Konec přeskakovaného textu -----


/*******************************************************************************
 * Třída <b><code>SprávcePlátna</code></b> slouží k jednoduchému kreslení
 * na virtuální plátno a připadné následné animaci nakreslených obrázků.
 * </p><p>
 * Třída neposkytuje veřejný konstruktor, protože chce, aby její instance
 * byla jedináček, tj. aby se všechno kreslilo na jedno a to samé plátno.
 * Jediným způsobem, jak získat odkaz na instanci třídy
 * <code>SprávcePlátna</code>,
 * je volaní její statické metody <code>getInstance()</code>.
 * </p><p>
 * Třída <code>SprávcePlátna</code> funguje jako manažer, který dohlíží
 * na to, aby se po změně zobrazení některého z tvarů všechny ostatní tvary
 * řádně překreslily, aby byly správně zachyceny všechny překryvy
 * a aby se při pohybu jednotlivé obrazce vzájemně neodmazavaly.
 * Aby vše správně fungovalo, je možno použít jeden dvou přístupů:</p>
 * <ul>
 *   <li>Manažer bude obsah plátna překreslovat
 *       <b>v pravidelných intervalech</b>
 *       bez ohledu na to, jestli se na něm udála nějaká změna či ne.
 *       <ul><li>
 *       <b>Výhodou</b> tohoto přístupu je, že se zobrazované objekty
 *       nemusí starat o to, aby se manažer dozvěděl, že se jejich stav změnil.
 *       </li><li>
 *       <b>Neýhodou</b> tohoto přístupu je naopak to, že manažer
 *       spotřebovává na neustálé překreslování jistou část výkonu
 *       procesoru, což může u pomalejších počítačů působit problémy.
 *       <br>&nbsp;</li></ul></li>
 *   <li>Manažer překresluje plátno <b>pouze na výslovné požádání</b>.
 *       <ul><li>
 *       <b>Výhodou</b> tohoto přístupu je úspora spotřebovaného výkonu
 *       počítače v období, kdy se na plátně nic neděje.
 *       </li><li>
 *       <b>Nevýhodou</b> tohoto přístupu je naopak to, že kreslené
 *       objekty musí na každou změnu svého stavu upozornit manažera,
 *       aby věděl, žed má plátno překreslit.
 *   </li>
 * </ul><p>
 * Třída <code>SprávcePlátna</code> požívá druhou z uvedených strategií,
 * tj. <b>překresluje plátno pouze na požádání</b>.
 * </p><p>
 * Obrazec, který chce být zobrazován na plátně, se musí nejprve přihlásit
 * u instance třídy <code>SprávcePlátna</code>, aby jej tato zařadila
 * mezi spravované obrazce (sada metod <code>přidej&hellip;</code>).
 * Přihlásit se však mohou pouze instance tříd, které implementují
 * rozhraní <code>IKreslený</code>.
 * </p><p>
 * Nepřihlášený obrazec nemá šanci býti zobrazen, protože na plátno
 * se může zobrazit pouze za pomoci kreslítka, jež může získat jedině od
 * instance třídy <code>SprávcePlátna</code>, ale ta je poskytuje pouze
 * instancím, které se přihlásily do její správy.
 * </p><p>
 * Obrazec, který již dále nemá byt kreslen, se muže odhlásit zavoláním
 * metody <code>odstraň(IKreslený)</code>.Zavoláním metody
 * <code>odstraňVše()</code> se ze seznamu spravovaných (a tím i z plátna)
 * odstraní všechny vykreslované obrazce.
 * </p><p>
 * Efektivitu vykreslování je možné ovlivnit voláním metody
 * <code>nekresli()</code>, která pozastaví překreslování plátna po nahlášených
 * změnách. Její volání je výhodné např. v situaci, kdy je třeba vykreslit
 * obrazec složený z řady menších obrazců a bylo by nevhodné překreslovat
 * plátno po vykreslení každého z nich.
 * </p><p>
 * Do původního stavu převedeme plátno voláním metody <code>vraťKresli()</code>,
 * která vrátí vykreslování do stavu před posledním voláním metody
 * <code>nekresli()</code>. Nemůžeč se tedy stát, že by se při zavolání metody
 * <code>nekresli()</code> v situaci, kdy je již vykreslování pozastaveno,
 * začalo po následém zavolání <code>vraťKresli()</code> hned vykreslovat.
 * Po dvou voláních <code>vraťKresli()</code> se začne vykreslovat až po
 * dvou zavoláních <code>vraťKresli()</code>.
 * </p><p>
 * Proto plátno pouze žádáme, aby se vrátilo do toho kreslícího stavu,
 * ve kterém bylo v okamžiku, kdy jsme je naposledy žádali o to,
 * aby se přestalo překreslovat. Nemůže se tedy stát, že by se při zavolání
 * metody <code>nekresli()</code> v situaci, kdy je již vykreslování
 * pozastaveno, začalo po následném zavolání <code>vraťKresli()</code> hned
 * vykreslovat.
 * </p><p>
 * Každé zavolání metody <code>nekresli()</code> musí být doplněno
 * odpovídajícím voláním <code>vraťKresli()</code>. Teprve když poslední
 * <code>vraťKresli()</code> odvolá první <code>nekresli()</code>, bude
 * překreslování opět obnoveno.
 * </p>
 *
 * @author Rudolf PECINOVSKÝ
 * @version 6.03.2017 — 2010-11-08
 */
public final class SprávcePlátna
{
//    static { System.out.println("Inicializace třídy SprávcePlátna"); }
//    { System.out.println("Inicializace instance třídy SprávcePlátna"); }
//== KONSTANTNÍ ATRIBUTY TŘÍDY =================================================

    /** Titulek okna aktivního plátna. */
    private static final String TITULEK_0  = "Plátno ovládané správcem";

    /** Počáteční políčková šířka aktivní plochy plátna. */
    private static final int ŠÍŘKA_0 = 6;

    /** Počáteční políčková výška aktivní plochy plátna. */
    private static final int VÝŠKA_0 = 6;

    /** Počáteční barva pozadí plátna. */
    private static final Barva POZADÍ_0 = Barva.KRÉMOVÁ;

    /** Počáteční barva čar mřížky. */
    private static final Barva BARVA_ČAR_0 = Barva.ČERNÁ;

    /** Implicitní rozteč čtvercové sítě. */
    private static final int KROK_0 = 50;

    /** Maximální povolená velikost rozteče čtvercové sítě. */
    private static final int MAX_KROK = 200;

    /** Jediná instance třídy SprávcePlátna. */
    private static final SprávcePlátna SP;  // = new SprávcePlátna();

    static {
        String klíč = "rup.io.testmode";
        if (System.getProperty(klíč) != null &&
            Boolean.valueOf(System.getProperty(klíč)))
        {
            SP = new SprávcePlátna(new SprávceTestovací());
        }
        else {
            SP = new SprávcePlátna(new SprávceBěžný());
        }
    }



//== PROMĚNNÉ ATRIBUTY TŘÍDY ===================================================
//== KONSTANTNÍ ATRIBUTY INSTANCÍ ==============================================

    /** Implemetnace {code ISprávce}, na kterou se deleguje.*/
    private final ISprávce delegát;



//== PROMĚNNÉ ATRIBUTY INSTANCÍ ================================================
//== PŘÍSTUPOVÉ METODY VLASTNOSTÍ TŘÍDY ========================================
//== OSTATNÍ NESOUKROMÉ METODY TŘÍDY ===========================================

//##############################################################################
//== KONSTRUKTORY A TOVÁRNÍ METODY =============================================

    /***************************************************************************
     * Jediná metoda umožnující získat odkaz na instanci plátna.
     * Vrací vsak pokaždé odkaz na stejnou instanci,
     * protože instance plátna je jedináček.
     * <p>
     * Pokud instance při volaní metody ještě neexistuje,
     * metoda instanci vytvoří.</p>
     *
     * @return Instance třídy SprávcePlátna
     */
    public static SprávcePlátna getInstance()
    {
        return SP;
    }


    /***************************************************************************
     * Vytvoří instanci třídy - je volaná pouze jednou.
     */
    @SuppressWarnings("serial")
    private SprávcePlátna(ISprávce delegát)
    {
        this.delegát = delegát;
    }



//== ABSTRAKTNÍ METODY =========================================================
//== PŘÍSTUPOVÉ METODY VLASTNOSTÍ INSTANCÍ =====================================

    /***************************************************************************
     * Nastaví rozměr plátna zadáním bodové velikosti políčka a
     * počtu políček ve vodorovném a svislém směru.
     * Při velikosti políčka = 1 se vypíná zobrazování mřížky.
     *
     * @param  krok    Nová bodová velikost políčka
     * @param  pŠířka  Nový počet políček vodorovně
     * @param  pVýška  Nový počet políček svisle
     */
    public synchronized void setKrokRozměr(int krok, int pŠířka, int pVýška)
    {
        delegát.setKrokRozměr(krok, pŠířka, pVýška, null);
    }


    /***************************************************************************
     * Nastaví rozměr plátna zadáním bodové velikosti políčka a
     * počtu políček ve vodorovném a svislém směru.
     * Při velikosti políčka = 1 se vypíná zobrazování mřížky.
     *
     * @param  krok    Nová bodová velikost políčka
     * @param  pŠířka  Nový počet políček vodorovně
     * @param  pVýška  Nový počet políček svisle
     * @param  měnič   Objekt, který žádá o změnu rozměru. Jakmile je jednou
     *                 tento objekt nastaven, nesmí již rozměr plátna
     *                 měnit nikdo jiný.
     */
//%I+ <135
//    private
//%I-
//%A+ >135
    public
//%A-
    synchronized void setKrokRozměr(int krok, int pŠířka, int pVýška,
                                    Object měnič)
    {
        delegát.setKrokRozměr(krok, pŠířka, pVýška, měnič);
    }


    /***************************************************************************
     * Vrátí vzdálenost čar mřížky = bodovou velikost políčka.
     *
     * @return Bodová velikost políčka
     */
     public synchronized int getKrok()
     {
         int krok = delegát.getKrok();
         return krok;
     }


    /***************************************************************************
     * Nastaví vzdálenost čar mřížky = bodovou velikost políčka.
     * Při velikosti políčka = 1 se vypíná zobrazování mřížky.
     *
     * @param velikost  Nová bodová velikost políčka
     */
//%I+ <135
//    private
//%I-
//%A+ >135
    public
//%A-
    synchronized void setKrok(int velikost)
    {
        delegát.setKrok(velikost);
    }


    /***************************************************************************
     * Vrátí počet sloupců plátna, tj. jeho políčkovou šířku.
     *
     * @return  Aktuální políčková šířka plátna (počet políček vodorovně)
     */
    public synchronized int getSloupců()
    {
        int    sloupců = delegát.getSloupců();
        return sloupců;
    }


    /***************************************************************************
     * Vrátí bodovou šířku plátna.
     *
     * @return  Aktuální bodová šířka plátna (počet bodů vodorovně)
     */
//%I+ <135
//    private
//%I-
//%A+ >135
    public
//%A-
    synchronized int getBšířka()
    {
        int    šířkaBodů = delegát.getBšířka();
        return šířkaBodů;
    }


    /***************************************************************************
     * Vrátí počet řádků plátna, tj. jeho políčkovou výšku.
     *
     * @return  Aktuální políčková výška plátna (počet políček svisle)
     */
    public synchronized int getŘádků()
    {
        int    řádků = delegát.getŘádků();
        return řádků;
    }


    /***************************************************************************
     * Vrátí bodovou výšku plátna.
     *
     * @return  Aktuální bodová výška plátna (počet bodů svisle)
     */
//%I+ <135
//    private
//%I-
//%A+ >135
    public
//%A-
    synchronized int getBVýška()
    {
        int    výškaBodů = delegát.getBVýška();
        return výškaBodů;
    }

//%A+ >135

    /***************************************************************************
     * Vrátí políčkový rozměr plátna, tj. šířku a výšku v polích.
     *
     * @return  Aktuální políčkový rozměr plátna
     */
    public synchronized Rozměr getRozměr()
    {
        return delegát.getRozměr();
    }

//%A-

    /***************************************************************************
     * Nastaví rozměr plátna zadáním jeho políčkové výsky a šířky.
     *
     * @param  sloupců  Nový počet políček vodorovně
     * @param  řádků  Nový počet políček svisle
     */
//%I+ <135
//    private
//%I-
//%A+ >135
    public
//%A-
    synchronized void setRozměr(int sloupců, int řádků)
    {
        delegát.setRozměr(sloupců, řádků);
    }

//%A+ >135

    /***************************************************************************
     * Nastaví rozměr plátna zadáním jeho políčkové výsky a šířky.
     *
     * @param  rozměr  Zadávaný rozměr v počtu políček
     */
    public synchronized void setRozměr(Rozměr rozměr)
    {
        delegát.setRozměr(rozměr.šířka, rozměr.výška);
    }

//%A-

    /***************************************************************************
     * Vrátí informaci o tom, je-li zobrazována mřížka.
     *
     * @return Mřížka je zobrazována = true, není zobrazována = false.
     */
    public synchronized boolean isMřížka()
    {
        boolean mřížka = delegát.isMřížka();
    	return  mřížka;
    }


    /***************************************************************************
     * V závislosti na hodntě parametru nastaví nebo potlačí
     * zobrazování čar mřížky.
     *
     * @param zobrazit  Jestli mřížku zobrazovat.
     */
    public synchronized void setMřížka(boolean zobrazit)
    {
        delegát.setMřížka(zobrazit);
    }


    /***************************************************************************
     * Poskytuje informaci o aktuální viditelnosti okna.
     *
     * @return Je-li okno viditelné, vrací <b>true</b>, jinak vrací <b>false</b>
     */
//%I+ <135
//    private
//%I-
//%A+ >135
    public
//%A-
    synchronized boolean isViditelné()
    {
        boolean is = delegát.isViditelné();
        return  is;
    }


    /***************************************************************************
     * V závislosti na hodntě parametru nastaví nebo potlačí viditelnost plátna.
     *
     * @param viditelné logická hodnota požadované viditelnost (true=viditelné)
     */
//%I+ <135
//    private
//%I-
//%A+ >135
    public
//%A-
    synchronized void setViditelné(boolean viditelné)
    {
        delegát.setViditelné(viditelné);
    }


    /***************************************************************************
     * Vrátí aktuální barvu pozadí.
     *
     * @return  Nastavena barva pozadí
     */
    public synchronized Barva getBarvaPozadí()
    {
        Barva   barvaPozadí = delegát.getBarvaPozadí();
        return  barvaPozadí;
    }


    /***************************************************************************
     * Nastaví pro plátno barvu pozadí.
     *
     * @param  barva  Nastavovaná barva pozadí
     */
    public synchronized void setBarvaPozadí(Barva barva)
    {
        delegát.setBarvaPozadí(barva);
    }


    /***************************************************************************
     * Pomocná metoda pro účely ladění aby bylo možno zkontrolovat,
     * ze na konci metody má semafor stejnou hodnotu, jako měl na počátku.
     *
     * @return  Stav vnitřního semaforu: >0  - nebude se kreslit,<br>
     *                                   ==0 - kreslí se,<br>
     *                                   <0  - chyba
     */
//%I+ <135
//    private
//%I-
//%A+ >135
    public
//%A-
    synchronized int getNekresli()
    {
        int     nekreslit = delegát.getNekresli();
        return  nekreslit;
    }


    /***************************************************************************
     * Vrátí aktuální název v titulkové liště okna plátna.
     *
     * @return  Aktuální název okna
     */
    public String getNázev()
    {
        String název = delegát.getNázev();
        return název;
    }


    /***************************************************************************
     * Nastaví název v titulkové liště okna plátna.
     *
     * @param název  Nastavovaný název
     */
    public void setNázev(String název)
    {
        delegát.setNázev(název);
    }

//%A+ >135

    /***************************************************************************
     * Vrátí vodorovnou souřadnici aplikačního okna plátna.
     *
     * @return Pozice levého horního rohu aplikačního okna plátna.
     */
    public Pozice getPozice()
    {
        Pozice  pozice = delegát.getPozice();
        return  pozice;
    }

//%A-

    /***************************************************************************
     * Nastaví pozici aplikačního okna aktivního plátna na obrazovce.
     *
     * @param x  Vodorovná souřadnice aplikačního okna plátna.
     * @param y  Svislá souřadnice aplikačního okna plátna.
     */
    public void setPozice(int x, int y)
    {
        delegát.setPozice(x, y);
    }

//%A+ >135

    /***************************************************************************
     * Nastaví pozici aplikačního okna aktivního plátna na obrazovce.
     *
     * @param pozice  Požadovaná pozice aplikačního okna plátna na obrazovce.
     */
    public void setPozice(Pozice pozice)
    {
        delegát.setPozice(pozice);
    }


    /***************************************************************************
     * Vrátí instanci třídy <code>Obrázek</code> zobrazující zadaný výřez
     * AktivníhoPlátna.
     * @param x     Vodorovná pozice požadovaného výřezu
     * @param y     Svislá pozice požadovaného výřezu
     * @param šířka Šířka požadovaného výřezu v bodech
     * @param výška Výška požadovaného výřezu v bodech
     * @return Instance třídy <code>Obrázek</code> zobrazující zadaný výřez
     */
    public Obrázek getObrázek(int x, int y, int šířka, int výška)
    {
        Obrázek obrázek = delegát.getObrázek(x, y, šířka, výška);
        return  obrázek;
    }

//%A-


//== OSTATNÍ NESOUKROMÉ METODY INSTANCÍ ========================================

    /***************************************************************************
     * Převede instanci na řetězec. Používá se především při ladění.
     *
     * @return Řetězcová reprezentace dané instance.
     */
    @Override
    public String toString()
    {
        return delegát.toString();
    }


    /***************************************************************************
     * Vykreslí všechny elementy.
     */
    public synchronized void překresli()
    {
        delegát.překresli();
    }


    /***************************************************************************
     * Potlačí překreslování plátna, přesněji zvýší hladinu potlačení
     * překreslování o jedničku. Návratu do stavu před voláním této metody
     * se dosáhne zavoláním metody <code>vraťKresli()</code>.</p>
     * <p>
     * Metody <code>nekresli()</code> a <code>vraťKresli()</code>
     * se tak chovají obdobně jako závorky, mezi nimiž je vykreslování
     * potlačeno.</p>
     */
    public synchronized void nekresli()
    {
        delegát.nekresli();
    }


    /***************************************************************************
     * Vrátí překreslování do stavu před posledním voláním metody
     * <code>nekresli()</code>. Předcházelo-li proto více volání metody
     * <code>nekresli()</code>, začne se překreslovat až po odpovídajím počtu
     * zavolání metody <code>vraťKresli()</code>.
     *
     * @throws IllegalStateException
     *         Je-li metoda volána aniž by předcházelo odpovídající volání
     *         <code>nekresli()</code>.
     */
    public synchronized void vraťKresli()
    {
        delegát.vraťKresli();
    }

//%A+ >135

    /***************************************************************************
     * Odpojí se od hodin a přestává synchronizovat
     * své překreslování s jejich tikotem.
     */
    public void vypniHodiny()
    {
        delegát.vypniHodiny();
    }


    /***************************************************************************
     * Napojí se na hodiny, aby se na začátku tiku vypnulo kreslení
     * a na konci tiku opět zapnulo.
     */
    public void zapniHodiny()
    {
        delegát.zapniHodiny();
    }

//%A-

    /***************************************************************************
     * Odstraní zadaný obrazec ze seznamu malovaných.
     * Byl-li obrazec v seznamu, překreslí plátno.
     *
     * @param obrazec  Odstraňovaný obrazec
     *
     * @return  true v případě, když obrazec v seznamu byl,
     *          false v případě, když nebylo co odstraňovat
     */
    public synchronized boolean odstraň(IKreslený obrazec)
    {
        boolean ret = delegát.odstraň(obrazec);
        return  ret;
    }


    /***************************************************************************
     * Vyčisti plátno, tj. vyprázdní seznam malovaných
     * (odstraní z něj všechny obrazce).
     */
    public synchronized void odstraňVše()
    {
        delegát.odstraňVše();
    }


    /***************************************************************************
     * Není-li zadaný obrazec v seznamu malovaných, přidá jej na konec
     * (bude se kreslit jako poslední, tj. na vrchu.
     * Byl-li obrazec opravdu přidán, překreslí plátno.
     * Objekty budou vždy kresleny v pořadí, v němž byly přidány do správy,
     * tj. v seznamu parametrů zleva doprava
     * a dříve zaregistrované objekty před objekty zaregistrovanými později.
     *
     * @param  obrazec  Přidávané obrazce
     * @return  Počet skutečně přidaných obrazců
     */
    public synchronized int přidej(IKreslený... obrazec)
    {
        int     přidáno = delegát.přidej(obrazec);
        return  přidáno;
    }


    /***************************************************************************
     * Přidá obrazec do seznamu malovaných tak, aby byl kreslen
     * nad zadaným obrazcem.
     * Pokud již v seznamu byl, jenom jej přesune do zadané pozice.
     *
     * @param  současný  Obrazec, který má byt při kreslení pod
     *                    přidávaným obrazcem
     * @param  přidaný   Přidávaný obrazec
     *
     * @return  true  v případě, když byl obrazec opravdu přidán,
     *          false v případě, když již mezi zobrazovanými byl
     *                a pouze se přesunul do jiné urovné
     */
    public synchronized boolean přidejNad(IKreslený současný, IKreslený přidaný)
    {
        boolean přidán = delegát.přidejNad(současný, přidaný);
        return  přidán;
    }


    /***************************************************************************
     * Přidá obrazec do seznamu malovaných tak, aby byl kreslen
     * pod zadaným obrazcem.
     * Pokud již v seznamu byl, jenom jej přesune do zadané pozice.
     *
     * @param  současný  Obrazec, který má byt při kreslení nad
     *                   přidávaným obrazcem
     * @param  přidaný   Přidávaný obrazec
     *
     * @return  true  v případě, když byl obrazec opravdu přidán,
     *          false v případě, když již mezi zobrazovanými byl
     *                a pouze se přesunul do jiné urovné
     */
    public synchronized boolean přidejPod(IKreslený současný, IKreslený přidaný)
    {
        boolean přidán = delegát.přidejPod(současný, přidaný);
        return  přidán;
    }


    /***************************************************************************
     * Přidá obrazec do seznamu malovaných tak, aby byl kreslen
     * nad všemi obrazci.
     * Pokud již v seznamu byl, jenom jej přesune do požadované pozice.
     *
     * @param  přidaný   Přidávaný obrazec
     *
     * @return  true  v případě, když byl obrazec opravdu přidán,
     *          false v případě, když již mezi zobrazovanými byl
     *                a pouze se přesunul do jiné urovné
     */
//%I+ <135
//    private
//%I-
//%A+ >135
    public
//%A-
    synchronized boolean přidejNavrch(IKreslený přidaný)
    {
        boolean přidán = delegát.přidejNavrch(přidaný);
        return  přidán;
    }


    /***************************************************************************
     * Přidá obrazec do seznamu malovaných tak, aby byl kreslen
     * pod zadaným obrazcem.
     * Pokud již v seznamu byl, jenom jej přesune do zadané pozice.
     *
     * @param  přidaný   Přidávaný obrazec
     *
     * @return  true  v případě, když byl obrazec opravdu přidán,
     *          false v případě, když již mezi zobrazovanými byl
     *                a pouze se přesunul do jiné urovné
     */
//%I+ <135
//    private
//%I-
//%A+ >135
    public
//%A-
    synchronized boolean přidejDospod(IKreslený přidaný)
    {
        boolean přidán = delegát.přidejDospod(přidaný);
        return  přidán;
    }


    /***************************************************************************
     * Vrátí pořadí zadaného prvku v seznamu kreslených prvků.
     * Prvky se přitom kreslí v rostoucím pořadí, takže obrazec
     * s větším poradím je kreslen nad obrazcem s menším poradím.
     * Neni-li zadaný obrazec mezi kreslenými, vrátí -1.
     *
     * @param  obrazec  Objekt, na jehož kreslicí pořadí se dotazujeme
     *
     * @return  Pořadí obrazce; prvý kresleny obrazec má pořadí 0.
     *          Neni-li zadaný obrazec mezi kreslenými, vrátí -1.
     */
//%I+ <135
//    private
//%I-
//%A+ >135
    public
//%A-
    synchronized int pořadí(IKreslený obrazec)
    {
        int     pořadí = delegát.pořadí(obrazec);
        return  pořadí;
    }


    /***************************************************************************
     * Vrátí nemodifikovatelný seznam všech spravovaných obrázků.
     *
     * @return  Požadovaný seznam
     */
//%I+ <135
//    private
//%I-
//%A+ >135
    public
//%A-
    List<IKreslený> seznamKreslených()
    {
        List<IKreslený> seznam = delegát.seznamKreslených();
        return          seznam;
    }

//%A+ >135

    /***************************************************************************
     * Přihlásí posluchače změny velikosti pole.
     *
     * @param  posluchač  Přihlašovaný posluchač
     * @return  Informace o tom, byl-li posluchač doopravdy přidán -
     *          false oznamuje, že posluchač už byl přihlášen
     *          a nebylo jej proto třeba přidávat.
     */
    public boolean přihlašPřizpůsobivý(IPřizpůsobivý posluchač)
    {
        boolean přihlášen = delegát.přihlašPřizpůsobivý(posluchač);
        return  přihlášen;
    }


    /***************************************************************************
     * Odhlásí posluchače změny velikosti pole.
     *
     * @param  posluchač  Odhlašovaný posluchač
     * @return  Informace o tom, byl-li posluchač doopravdy odebrán -
     *          false oznamuje, že posluchač už nebyl přihlášen
     *          a nebylo jej proto třeba odebírat.
     */
    public boolean odhlašPřizpůsobivý(IPřizpůsobivý posluchač)
    {
        boolean odhlášen = delegát.odhlašPřizpůsobivý(posluchač);
        return  odhlášen;
    }


    /***************************************************************************
     * Nastaví, zda se mají přihlášeným posluchačům hlásit změny
     * velikosti kroku a vrátí původní nastavení.
     *
     * @param hlásit  Požadované nastavení (true=hlásit, false=nehlasit).
     * @return Původní nastavení
     */
    public boolean hlásitZměnyRozměru(boolean hlásit)
    {
        boolean ret = delegát.hlásitZměnyRozměru(hlásit);
        return  ret;
    }

//%A-

    /***************************************************************************
     * Přihlásí posluchače událostí klávesnice.
     *
     * @param posluchač  Přihlašovaný posluchač
     */
//%I+ <135
//    private
//%I-
//%A+ >135
    public
//%A-
    void přihlašKlávesnici(KeyListener posluchač)
    {
        delegát.přihlašKlávesnici(posluchač);
    }


    /***************************************************************************
     * Odhlásí posluchače klávesnice.
     *
     * @param posluchač  Odhlašovaný posluchač
     */
//%I+ <135
//    private
//%I-
//%A+ >135
    public
//%A-
    void odhlašKlávesnici(KeyListener posluchač)
    {
        delegát.odhlašKlávesnici(posluchač);
    }


    /***************************************************************************
     * Přihlásí posluchače událostí myši.
     *
     * @param posluchač  Přihlašovaný posluchač
     */
//%I+ <135
//    private
//%I-
//%A+ >135
    public
//%A-
    void přihlašMyš(MouseListener posluchač)
    {
        delegát.přihlašMyš(posluchač);
    }


    /***************************************************************************
     * Odhlásí posluchače myši.
     *
     * @param posluchač  Odhlašovaný posluchač
     */
//%I+ <135
//    private
//%I-
//%A+ >135
    public
//%A-
    void odhlašMyš(MouseListener posluchač)
    {
        delegát.odhlašMyš(posluchač);
    }


    /***************************************************************************
     * Uloží obraz aktivního plátna do zadaného souboru.
     *
     * @param soubor Soubor, do nějž se má obraz plátna uložit
     */
//%I+ <135
//    private
//%I-
//%A+ >135
    public
//%A-
    void uložJakoObrázek(File soubor)
    {
        delegát.uložJakoObrázek(soubor);
    }


//== SOUKROMÉ A POMOCNÉ METODY TŘÍDY ===========================================
//== SOUKROMÉ A POMOCNÉ METODY INSTANCÍ ========================================
//== VNOŘENÉ A VNITŘNÍ TŘÍDY ===================================================

    /***************************************************************************
     * Rozhraní {@code IPlátno} deklaruje kompletní sadu metod
     * implementovaných třídou {@code Plátno}.
     * Představuje společného rodiče výkonné a testovací třídy,
     * tj. třídy {@code PlátnoBěžné} a třídy {@code PlátnoTestovací}.
     */
    private static interface ISprávce
    {
    //== KONSTANTY =============================================================
    //== DEKLAROVANÉ METODY ====================================================

    public void     setKrokRozměr(int krok, int pŠířka, int pVýška);
    public void     setPozice    (int x, int y);
    public int      getKrok      ();
    public int      getSloupců   ();
    public int      getŘádků     ();
    public boolean  isMřížka     ();
    public void     setMřížka    (boolean zobrazit);
    public Barva    getBarvaPozadí();
    public void     setBarvaPozadí(Barva barva);
    public String   getNázev();
    public void     setNázev(String název);
//%A+ >135
    public void   setKrokRozměr(int krok, int pŠířka, int pVýška, Object měnič);
    public void     setKrok(int velikost);
    public int      getBšířka();
    public int      getBVýška();
    public Rozměr   getRozměr();
    public void     setRozměr(int sloupců, int řádků);
    public void     setRozměr(Rozměr rozměr);
    public boolean  isViditelné();
    public void     setViditelné(boolean viditelné);
    public int      getNekresli();
    public Pozice   getPozice();
    public void     setPozice(Pozice pozice);
    public Obrázek  getObrázek(int x, int y, int šířka, int výška);
//%A-

    public void     překresli();
    public void     nekresli();
    public void     vraťKresli();
    public boolean  odstraň(IKreslený obrazec);
    public void     odstraňVše();
    public int      přidej(IKreslený... obrazec);
    public boolean  přidejNad(IKreslený současný, IKreslený přidaný);
    public boolean  přidejPod(IKreslený současný, IKreslený přidaný);
//%A+ >135
    public void     vypniHodiny();
    public void     zapniHodiny();
    public boolean  přidejNavrch(IKreslený přidaný);
    public boolean  přidejDospod(IKreslený přidaný);
    public int      pořadí(IKreslený obrazec);
    public List<IKreslený> seznamKreslených();
    public boolean  přihlašPřizpůsobivý(IPřizpůsobivý posluchač);
    public boolean  odhlašPřizpůsobivý(IPřizpůsobivý posluchač);
    public boolean  hlásitZměnyRozměru(boolean hlásit);
    public void     přihlašKlávesnici(KeyListener posluchač);
    public void     odhlašKlávesnici(KeyListener posluchač);
    public void     přihlašMyš(MouseListener posluchač);
    public void     odhlašMyš(MouseListener posluchač);
    public void     uložJakoObrázek(File soubor);
//%A-

    //== ZDĚDĚNÉ METODY ========================================================
    //== INTERNÍ DATOVÉ TYPY ===================================================
    }


///#############################################################################
///#############################################################################
///#############################################################################


    static private final class SprávceBěžný implements ISprávce
    {
    //    static { System.out.println("Inicializace třídy SprávcePlátna"); }
    //    { System.out.println("Inicializace instance třídy SprávcePlátna"); }
    //== KONSTANTNÍ ATRIBUTY TŘÍDY =================================================

        /** Titulek okna aktivního plátna. */
        private static final String TITULEK_0  = "Plátno ovládané správcem";

        /** Počáteční políčková šířka aktivní plochy plátna. */
        private static final int ŠÍŘKA_0 = 6;

        /** Počáteční políčková výška aktivní plochy plátna. */
        private static final int VÝŠKA_0 = 6;

        /** Počáteční barva pozadí plátna. */
        private static final Barva POZADÍ_0 = Barva.KRÉMOVÁ;

        /** Počáteční barva čar mřížky. */
        private static final Barva BARVA_ČAR_0 = Barva.ČERNÁ;

        /** Implicitní rozteč čtvercové sítě. */
        private static final int KROK_0 = 50;

        /** Maximální povolená velikost rozteče čtvercové sítě. */
        private static final int MAX_KROK = 200;

//        /** Jediná instance třídy SprávcePlátna. */
//        private static final SprávcePlátna SP = new SprávcePlátna();



    //== PROMĚNNÉ ATRIBUTY TŘÍDY ===================================================
    //== KONSTANTNÍ ATRIBUTY INSTANCÍ ==============================================

        /** Aplikační okno animačního plátna. */
        private final JFrame okno;

        /** Instance lokální třídy, která je zřízena proto, aby odstínila
         *  metody svého rodiče JPanel. */
        private final JPanel plátno;

        /** Seznam zobrazovaných předmětů. */
        private final List<IKreslený> předměty = new ArrayList<IKreslený>();
    //%A+ >135

        /** Seznam přihlášených přizpůsobivých předmětů. */
        private final Collection<IPřizpůsobivý> přizpůsobiví =
                                                new ArrayList<IPřizpůsobivý>();
    //%A-



    //== PROMĚNNÉ ATRIBUTY INSTANCÍ ================================================

        //Z venku neovlivnitelné Atributy pro zobrazeni plátna v aplikačním okně

            /** Vše se kreslí na obraz - ten se snadněji překreslí. */
            private Image obrazPlátna;

            /** Kreslítko získané od obrazu plátna, na nějž se vlastně kreslí. */
            private Kreslítko kreslítko;

            /** Semafor bránící příliš častému překreslování. Překresluje se pouze
             *  je-li ==0. Nesmi byt <0. */
            private int nekreslit = 0;

            /** Příznak toho, že kreslení právě probíhá,
             *  takže vypínání nefunguje. */
            private boolean kreslím = false;

            /** Čáry zobrazující na plántě mřížku. */
            private Čára[] vodorovná,   svislá;

        //Přímo ovlivnitelné atributy

            /** Rozteč čtvercové sítě. */
            private int krok = KROK_0;

            /** Zobrazuje-li se mřížka. */
            private boolean mřížka = true;

            /** Barva pozadí plátna. */
            private Barva barvaPozadí = POZADÍ_0;

            /** Barva čar mřížky. */
            private Barva barvaČar = BARVA_ČAR_0;

            /** Šířka aktivní plochy plátna v udávaná v polích. */
            private int sloupců = ŠÍŘKA_0;

            /** Výška aktivní plochy plátna v udávaná v polích. */
            private int řádků = VÝŠKA_0;

            /** Šířka aktivní plochy plátna v bodech. */
            private int šířkaBodů = ŠÍŘKA_0 * krok;

            /** Výška aktivní plochy plátna v bodech. */
            private int výškaBodů = VÝŠKA_0 * krok;
    //%A+ >135

            /** Zda se mají přizpůsobiví upozorňovat na změny rozměru pole. */
            private boolean hlásitZměnyRozměru = true;
    //%A-

            /** Zda je možno měnit velikost kroku. */
            private Object vlastníkPovoleníZměnyKroku = null;

            /** Název v titulkové liště animačního plátna. */
            private String název  = TITULEK_0;

            /** Pozice plátna na obrazovace - při používání více obrazovek
             *  je občas třeba ji po zviditelnění obnovit. */
            Point pozice = new Point(0, 0);



    //== PŘÍSTUPOVÉ METODY VLASTNOSTÍ TŘÍDY ========================================
    //== OSTATNÍ NESOUKROMÉ METODY TŘÍDY ===========================================

    //##############################################################################
    //== KONSTRUKTORY A TOVÁRNÍ METODY =============================================

        /***************************************************************************
         * Jediná metoda umožnující získat odkaz na instanci plátna.
         * Vrací vsak pokaždé odkaz na stejnou instanci,
         * protože instance plátna je jedináček.
         * <p>
         * Pokud instance při volaní metody ještě neexistuje,
         * metoda instanci vytvoří.</p>
         *
         * @return Instance třídy SprávcePlátna
         */
        public static SprávcePlátna getInstance()
        {
            return SP;
        }


        /***************************************************************************
         * Vytvoří instanci třídy - je volaná pouze jednou.
         */
        @SuppressWarnings("serial")
        private SprávceBěžný()
        {
            okno  = new JFrame();          //Vytvoří nové aplikační okno
            okno.setTitle(název);
            konfiguraceZeSouboru();

            //Zavřením okna se zavře celá aplikace
            okno.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

            //Vlastní plátno je definováno jako instance anonymní třídy
            plátno =
                new JPanel()
                {   /** Povinně překrývaná abstraktní metoda třídy JPanel. */
                    @Override
                    public synchronized void paintComponent(Graphics g) {
                        g.drawImage(obrazPlátna, 0, 0, null);
                    }
                };//Konec definice třídy plátna
            okno.setContentPane(plátno);

        //Při kreslení čar se ptá APosuvný po správci plánta. Proto se mohou čáry
        //kreslit až poté, co bude jednináček (konstanta SP) inicializován.
            //Připraví a vykreslí prázdné plátno
            SP.setKrokRozměr(KROK_0, ŠÍŘKA_0, VÝŠKA_0);
            //Nyní je znám rozměr plátna, tak můžeme umístit dialogová okna
            int x = okno.getX();
            int y = okno.getY() + okno.getHeight();
            IO.oknaNa(x, y);
        }



    //== ABSTRAKTNÍ METODY =========================================================
    //== PŘÍSTUPOVÉ METODY VLASTNOSTÍ INSTANCÍ =====================================

        /***************************************************************************
         * Nastaví rozměr plátna zadáním bodové velikosti políčka a
         * počtu políček ve vodorovném a svislém směru.
         * Při velikosti políčka = 1 se vypíná zobrazování mřížky.
         *
         * @param  krok    Nová bodová velikost políčka
         * @param  pŠířka  Nový počet políček vodorovně
         * @param  pVýška  Nový počet políček svisle
         */
        @Override
        public synchronized void setKrokRozměr(int krok, int pŠířka, int pVýška)
        {
            setKrokRozměr(krok, pŠířka, pVýška, null);
        }


        /***************************************************************************
         * Nastaví rozměr plátna zadáním bodové velikosti políčka a
         * počtu políček ve vodorovném a svislém směru.
         * Při velikosti políčka = 1 se vypíná zobrazování mřížky.
         *
         * @param  krok    Nová bodová velikost políčka
         * @param  pŠířka  Nový počet políček vodorovně
         * @param  pVýška  Nový počet políček svisle
         * @param  měnič   Objekt, který žádá o změnu rozměru. Jakmile je jednou
         *                 tento objekt nastaven, nesmí již rozměr plátna
         *                 měnit nikdo jiný.
         */
    //%I+ <135
    //    private
    //%I-
    //%A+ >135
        public
    //%A-
        @Override
        synchronized void setKrokRozměr(int krok, int pŠířka, int pVýška,
                                        Object měnič)
        {
            //Kontrola, jetli rozměry mění ten, kdo je měnit smí
            if ((měnič != null)  &&
                (měnič != vlastníkPovoleníZměnyKroku))
            {
                if (vlastníkPovoleníZměnyKroku == null) {
                    vlastníkPovoleníZměnyKroku = měnič;
                } else {
                    throw new IllegalStateException(
                        "Změna kroku a rozměru není danému objektu povolena");
                }
            }
            //Ověření korektnosti zadaných parametrů
            Dimension obrazovka = Toolkit.getDefaultToolkit().getScreenSize();
            if ((krok   < 1)  ||
                (pŠířka < 2)  ||  (obrazovka.width  < šířkaBodů) ||
                (pVýška < 2)  ||  (obrazovka.height < výškaBodů))
            {
                throw new IllegalArgumentException(
                    "\nŠpatně zadané rozměry: " +
                    "\n  krok =" + krok  + " bodů," +
                    "\n  šířka=" + pŠířka + " polí = " + pŠířka*krok + " bodů," +
                    "\n  výška=" + pVýška + " polí = " + pVýška*krok + " bodů," +
                    "\n  obrazovka= " + obrazovka.width  + "×" +
                                        obrazovka.height + " bodů\n");
            }

            šířkaBodů = pŠířka * krok;
            výškaBodů = pVýška * krok;

            okno.setResizable(true);
            plátno.setPreferredSize(new Dimension(šířkaBodů, výškaBodů));
            okno.pack();
            okno.setResizable(false);

            obrazPlátna = plátno.createImage(šířkaBodů, výškaBodů);
            kreslítko   = new Kreslítko((Graphics2D)obrazPlátna.getGraphics());
            kreslítko.setPozadí(barvaPozadí);

            int starý    = this.krok;
            this.krok    = krok;
            this.sloupců = pŠířka;
            this.řádků   = pVýška;

            připravČáry();

    //%A+ >135
            if (hlásitZměnyRozměru  &&  (krok != starý))
            {
                nekreslit++; {
                    for (IPřizpůsobivý ip : přizpůsobiví) {
                        ip.krokZměněn(starý, krok);
                    }
                }nekreslit--;
            }
    //%A-
            IO.Oprava.rozměrOkna(okno);
            IO.Oprava.poziceOkna(okno);
            setViditelné(true);
    //        překresli();  //Překresluje již předchozí metoda
        }


        /***************************************************************************
         * Vrátí vzdálenost čar mřížky = bodovou velikost políčka.
         *
         * @return Bodová velikost políčka
         */
         @Override public synchronized int getKrok()
         {
             return krok;
         }


        /***************************************************************************
         * Nastaví vzdálenost čar mřížky = bodovou velikost políčka.
         * Při velikosti políčka = 1 se vypíná zobrazování mřížky.
         *
         * @param velikost  Nová bodová velikost políčka
         */
    //%I+ <135
    //    private
    //%I-
    //%A+ >135
        @Override public
    //%A-
        synchronized void setKrok(int velikost)
        {
            setKrokRozměr(velikost, sloupců, řádků);
        }


        /***************************************************************************
         * Vrátí počet sloupců plátna, tj. jeho políčkovou šířku.
         *
         * @return  Aktuální políčková šířka plátna (počet políček vodorovně)
         */
        @Override public synchronized int getSloupců()
        {
            return sloupců;
        }


        /***************************************************************************
         * Vrátí bodovou šířku plátna.
         *
         * @return  Aktuální bodová šířka plátna (počet bodů vodorovně)
         */
    //%I+ <135
    //    private
    //%I-
    //%A+ >135
        @Override public
    //%A-
        synchronized int getBšířka()
        {
            return šířkaBodů;
        }


        /***************************************************************************
         * Vrátí počet řádků plátna, tj. jeho políčkovou výšku.
         *
         * @return  Aktuální políčková výška plátna (počet políček svisle)
         */
        @Override public synchronized int getŘádků()
        {
            return řádků;
        }


        /***************************************************************************
         * Vrátí bodovou výšku plátna.
         *
         * @return  Aktuální bodová výška plátna (počet bodů svisle)
         */
    //%I+ <135
    //    private
    //%I-
    //%A+ >135
        @Override public
    //%A-
        synchronized int getBVýška()
        {
            return výškaBodů;
        }

    //%A+ >135

        /***************************************************************************
         * Vrátí políčkový rozměr plátna, tj. šířku a výšku v polích.
         *
         * @return  Aktuální políčkový rozměr plátna
         */
        @Override public synchronized Rozměr getRozměr()
        {
            return new Rozměr(sloupců, řádků);
        }

    //%A-

        /***************************************************************************
         * Nastaví rozměr plátna zadáním jeho políčkové výsky a šířky.
         *
         * @param  sloupců  Nový počet políček vodorovně
         * @param  řádků  Nový počet políček svisle
         */
    //%I+ <135
    //    private
    //%I-
    //%A+ >135
        @Override public
    //%A-
        synchronized void setRozměr(int sloupců, int řádků)
        {
            setKrokRozměr(krok, sloupců, řádků);
        }

    //%A+ >135

        /***************************************************************************
         * Nastaví rozměr plátna zadáním jeho políčkové výsky a šířky.
         *
         * @param  rozměr  Zadávaný rozměr v počtu políček
         */
        @Override public synchronized void setRozměr(Rozměr rozměr)
        {
            setRozměr(rozměr.šířka, rozměr.výška);
        }

    //%A-

        /***************************************************************************
         * Vrátí informaci o tom, je-li zobrazována mřížka.
         *
         * @return Mřížka je zobrazována = true, není zobrazována = false.
         */
        @Override public synchronized boolean isMřížka()
        {
            return mřížka;
        }


        /***************************************************************************
         * V závislosti na hodntě parametru nastaví nebo potlačí
         * zobrazování čar mřížky.
         *
         * @param zobrazit  Jestli mřížku zobrazovat.
         */
        @Override public synchronized void setMřížka(boolean zobrazit)
        {
            mřížka = zobrazit;
            připravČáry();
            překresli();
        }


        /***************************************************************************
         * Poskytuje informaci o aktuální viditelnosti okna.
         *
         * @return Je-li okno viditelné, vrací <b>true</b>, jinak vrací <b>false</b>
         */
    //%I+ <135
    //    private
    //%I-
    //%A+ >135
        @Override public
    //%A-
        synchronized boolean isViditelné()
        {
            return okno.isVisible();
        }


        /***************************************************************************
         * V závislosti na hodntě parametru nastaví nebo potlačí viditelnost plátna.
         *
         * @param viditelné logická hodnota požadované viditelnost (true=viditelné)
         */
    //%I+ <135
    //    private
    //%I-
    //%A+ >135
        @Override public
    //%A-
        synchronized void setViditelné(boolean viditelné)
        {
            boolean změna = (isViditelné() != viditelné);
            if (změna)
            {
                pozice = okno.getLocation();
                okno.setVisible(viditelné);
                if (viditelné)
                {
                    //Při více obrazovkách po zviáditelnění blbne =>
                    okno.setLocation(pozice);   //je třeba znovu nastavit pozici
                    okno.setAlwaysOnTop(true);
                    okno.toFront();
                    překresli();
                    okno.setAlwaysOnTop(false);
                }
            }
        }


        /***************************************************************************
         * Vrátí aktuální barvu pozadí.
         *
         * @return  Nastavena barva pozadí
         */
        @Override public synchronized Barva getBarvaPozadí()
        {
            return barvaPozadí;
        }


        /***************************************************************************
         * Nastaví pro plátno barvu pozadí.
         *
         * @param  barva  Nastavovaná barva pozadí
         */
        @Override public synchronized void setBarvaPozadí(Barva barva)
        {
            barvaPozadí = barva;
            kreslítko.setPozadí(barvaPozadí);
            překresli();
        }


        /***************************************************************************
         * Pomocná metoda pro účely ladění aby bylo možno zkontrolovat,
         * ze na konci metody má semafor stejnou hodnotu, jako měl na počátku.
         *
         * @return  Stav vnitřního semaforu: >0  - nebude se kreslit,<br>
         *                                   ==0 - kreslí se,<br>
         *                                   <0  - chyba
         */
    //%I+ <135
    //    private
    //%I-
    //%A+ >135
        @Override public
    //%A-
        synchronized int getNekresli()
        {
            return nekreslit;
        }


        /***************************************************************************
         * Vrátí aktuální název v titulkové liště okna plátna.
         *
         * @return  Aktuální název okna
         */
        @Override public String getNázev()
        {
            return okno.getTitle();
        }


        /***************************************************************************
         * Nastaví název v titulkové liště okna plátna.
         *
         * @param název  Nastavovaný název
         */
        @Override public void setNázev(String název)
        {
            okno.setTitle(this.název = název);
        }

    //%A+ >135

        /***************************************************************************
         * Vrátí vodorovnou souřadnici aplikačního okna plátna.
         *
         * @return Pozice levého horního rohu aplikačního okna plátna.
         */
        @Override public Pozice getPozice()
        {
            return new Pozice(okno.getX(), okno.getY());
        }

    //%A-

        /***************************************************************************
         * Nastaví pozici aplikačního okna aktivního plátna na obrazovce.
         *
         * @param x  Vodorovná souřadnice aplikačního okna plátna.
         * @param y  Svislá souřadnice aplikačního okna plátna.
         */
        @Override public void setPozice(int x, int y)
        {
            okno.setLocation(x, y);
            pozice = new Point(x, y);
        }

    //%A+ >135

        /***************************************************************************
         * Nastaví pozici aplikačního okna aktivního plátna na obrazovce.
         *
         * @param pozice  Požadovaná pozice aplikačního okna plátna na obrazovce.
         */
        @Override public void setPozice(Pozice pozice)
        {
            okno.setLocation(pozice.getX(), pozice.getY());
        }


        /***************************************************************************
         * Vrátí instanci třídy <code>Obrázek</code> zobrazující zadaný výřez
         * AktivníhoPlátna.
         * @param x     Vodorovná pozice požadovaného výřezu
         * @param y     Svislá pozice požadovaného výřezu
         * @param šířka Šířka požadovaného výřezu v bodech
         * @param výška Výška požadovaného výřezu v bodech
         * @return Instance třídy <code>Obrázek</code> zobrazující zadaný výřez
         */
        @Override public Obrázek getObrázek(int x, int y, int šířka, int výška)
        {
            BufferedImage bim = getBufferedImage(x, y, šířka, výška);
            return new Obrázek(0, 0, bim);
        }

    //%A-


    //== OSTATNÍ NESOUKROMÉ METODY INSTANCÍ ========================================

        /***************************************************************************
         * Převede instanci na řetězec. Používá se především při ladění.
         *
         * @return Řetězcová reprezentace dané instance.
         */
        @Override public String toString()
        {
            return getClass().getName() + "(krok=" + krok +
                    ", šířka=" + sloupců + ", výška=" + řádků +
                    ", pozadí=" + barvaPozadí + ")";
        }


        /***************************************************************************
         * Vykreslí všechny elementy.
         */
        @Override public synchronized void překresli()
        {
            if (kreslím) {    //Právě překresluji - volám nepřímo sám sebe
                return;
            }
            if ((nekreslit == 0)  &&  isViditelné())   //Mám kreslit a je proč
            {
                kreslím = true;
                synchronized(plátno)
                {
                    kreslítko.vyplňRám(0, 0, šířkaBodů, výškaBodů,
                                        barvaPozadí);
                    if (mřížka  &&  (barvaČar != barvaPozadí))
                    {
                        //Budeme kreslit mřížku -- bude pod obrazci
                        for (int i=0;   i < sloupců;  ) {
                            svislá[i++].nakresli(kreslítko);
                        }
                        for (int i=0;   i < řádků;  ) {
                            vodorovná[i++].nakresli(kreslítko);
                        }
                    }
                    for (IKreslený předmět : předměty) {
                        předmět.nakresli(kreslítko);
                    }
                }//synchronized(obrazPlátna)
                plátno.repaint();       //Překreslí se aktualizované plátno
                kreslím = false;        //Už nekreslím
            }
        }


        /***************************************************************************
         * Potlačí překreslování plátna, přesněji zvýší hladinu potlačení
         * překreslování o jedničku. Návratu do stavu před voláním této metody
         * se dosáhne zavoláním metody <code>vraťKresli()</code>.</p>
         * <p>
         * Metody <code>nekresli()</code> a <code>vraťKresli()</code>
         * se tak chovají obdobně jako závorky, mezi nimiž je vykreslování
         * potlačeno.</p>
         */
        @Override public synchronized void nekresli()
        {
            nekreslit++;
        }


        /***************************************************************************
         * Vrátí překreslování do stavu před posledním voláním metody
         * <code>nekresli()</code>. Předcházelo-li proto více volání metody
         * <code>nekresli()</code>, začne se překreslovat až po odpovídajím počtu
         * zavolání metody <code>vraťKresli()</code>.
         *
         * @throws IllegalStateException
         *         Je-li metoda volána aniž by předcházelo odpovídající volání
         *         <code>nekresli()</code>.
         */
        @Override public synchronized void vraťKresli()
        {
            if (nekreslit == 0) {
                throw new IllegalStateException(
                    "Vrácení do stavu kreslení musí přecházet zákaz!");
            }
            nekreslit--;
            if (nekreslit == 0)  {
                překresli();
            }
        }

    //%A+ >135

        /***************************************************************************
         * Odpojí se od hodin a přestává synchronizovat
         * své překreslování s jejich tikotem.
         */
        @Override public void vypniHodiny()
        {
            Hodiny hodiny = Hodiny.getInstance();
            hodiny.setÚvod (null);
            hodiny.setZávěr(null);
        }


        /***************************************************************************
         * Napojí se na hodiny, aby se na začátku tiku vypnulo kreslení
         * a na konci tiku opět zapnulo.
         */
        @Override public void zapniHodiny()
        {
            Hodiny hodiny = Hodiny.getInstance();
            hodiny.setÚvod(new IČasovaný() {
                @Override
                public void tik() { nekresli(); }
            });
            hodiny.setZávěr(new IČasovaný() {
                @Override
                public void tik() { vraťKresli(); }
            });
        }

    //%A-

        /***************************************************************************
         * Odstraní zadaný obrazec ze seznamu malovaných.
         * Byl-li obrazec v seznamu, překreslí plátno.
         *
         * @param obrazec  Odstraňovaný obrazec
         *
         * @return  true v případě, když obrazec v seznamu byl,
         *          false v případě, když nebylo co odstraňovat
         */
        @Override public synchronized boolean odstraň(IKreslený obrazec)
        {
            boolean ret = předměty.remove(obrazec);
            if (ret) {
                překresli();
            }
            return ret;
        }


        /***************************************************************************
         * Vyčisti plátno, tj. vyprázdní seznam malovaných
         * (odstraní z něj všechny obrazce).
         */
        @Override public synchronized void odstraňVše()
        {
            nekresli(); {
                ListIterator<IKreslený> it = předměty.listIterator();
                while (it.hasNext()) {
                    it.next();
                    it.remove();
                }
            } vraťKresli();
        }


        /***************************************************************************
         * Není-li zadaný obrazec v seznamu malovaných, přidá jej na konec
         * (bude se kreslit jako poslední, tj. na vrchu.
         * Byl-li obrazec opravdu přidán, překreslí plátno.
         * Objekty budou vždy kresleny v pořadí, v němž byly přidány do správy,
         * tj. v seznamu parametrů zleva doprava
         * a dříve zaregistrované objekty před objekty zaregistrovanými později.
         *
         * @param  obrazec  Přidávané obrazce
         * @return  Počet skutečně přidaných obrazců
         */
        @Override public synchronized int přidej(IKreslený... obrazec)
        {
            int počet = 0;
            nekresli(); {
                for (IKreslený ik : obrazec)
                {
                    if (! předměty.contains(ik)) {
                        předměty.add(ik);
                        počet++;
                    }
                }
            } vraťKresli();
            return počet;
        }


        /***************************************************************************
         * Přidá obrazec do seznamu malovaných tak, aby byl kreslen
         * nad zadaným obrazcem.
         * Pokud již v seznamu byl, jenom jej přesune do zadané pozice.
         *
         * @param  současný  Obrazec, který má byt při kreslení pod
         *                    přidávaným obrazcem
         * @param  přidaný   Přidávaný obrazec
         *
         * @return  true  v případě, když byl obrazec opravdu přidán,
         *          false v případě, když již mezi zobrazovanými byl
         *                a pouze se přesunul do jiné urovné
         */
        @Override public synchronized boolean přidejNad(IKreslený současný, IKreslený přidaný)
        {
            boolean nebyl = ! předměty.remove(přidaný);
            int kam = předměty.indexOf(současný);
            if (kam < 0)
            {
                throw new IllegalArgumentException(
                    "Referenční objekt není na plátně zobrazován!");
            }
            předměty.add(kam+1, přidaný);
            překresli();
            return nebyl;
        }


        /***************************************************************************
         * Přidá obrazec do seznamu malovaných tak, aby byl kreslen
         * pod zadaným obrazcem.
         * Pokud již v seznamu byl, jenom jej přesune do zadané pozice.
         *
         * @param  současný  Obrazec, který má byt při kreslení nad
         *                   přidávaným obrazcem
         * @param  přidaný   Přidávaný obrazec
         *
         * @return  true  v případě, když byl obrazec opravdu přidán,
         *          false v případě, když již mezi zobrazovanými byl
         *                a pouze se přesunul do jiné urovné
         */
        @Override public synchronized boolean přidejPod(IKreslený současný, IKreslený přidaný)
        {
            boolean nebyl = ! předměty.remove(přidaný);
            int kam = předměty.indexOf(současný);
            if (kam < 0)
            {
                throw new IllegalArgumentException(
                    "Referenční objekt není na plátně zobrazován!");
            }
            předměty.add(kam, přidaný);
            překresli();
            return nebyl;
        }


        /***************************************************************************
         * Přidá obrazec do seznamu malovaných tak, aby byl kreslen
         * nad všemi obrazci.
         * Pokud již v seznamu byl, jenom jej přesune do požadované pozice.
         *
         * @param  přidaný   Přidávaný obrazec
         *
         * @return  true  v případě, když byl obrazec opravdu přidán,
         *          false v případě, když již mezi zobrazovanými byl
         *                a pouze se přesunul do jiné urovné
         */
    //%I+ <135
    //    private
    //%I-
    //%A+ >135
        @Override public
    //%A-
        synchronized boolean přidejNavrch(IKreslený přidaný)
        {
            boolean nebyl = ! předměty.remove(přidaný);
            předměty.add(přidaný);
            překresli();
            return nebyl;
        }


        /***************************************************************************
         * Přidá obrazec do seznamu malovaných tak, aby byl kreslen
         * pod zadaným obrazcem.
         * Pokud již v seznamu byl, jenom jej přesune do zadané pozice.
         *
         * @param  přidaný   Přidávaný obrazec
         *
         * @return  true  v případě, když byl obrazec opravdu přidán,
         *          false v případě, když již mezi zobrazovanými byl
         *                a pouze se přesunul do jiné urovné
         */
    //%I+ <135
    //    private
    //%I-
    //%A+ >135
        @Override public
    //%A-
        synchronized boolean přidejDospod(IKreslený přidaný)
        {
            boolean nebyl = ! předměty.remove(přidaný);
            předměty.add(0, přidaný);
            překresli();
            return nebyl;
        }


        /***************************************************************************
         * Vrátí pořadí zadaného prvku v seznamu kreslených prvků.
         * Prvky se přitom kreslí v rostoucím pořadí, takže obrazec
         * s větším poradím je kreslen nad obrazcem s menším poradím.
         * Neni-li zadaný obrazec mezi kreslenými, vrátí -1.
         *
         * @param  obrazec  Objekt, na jehož kreslicí pořadí se dotazujeme
         *
         * @return  Pořadí obrazce; prvý kresleny obrazec má pořadí 0.
         *          Neni-li zadaný obrazec mezi kreslenými, vrátí -1.
         */
    //%I+ <135
    //    private
    //%I-
    //%A+ >135
        @Override public
    //%A-
        synchronized int pořadí(IKreslený obrazec)
        {
            return předměty.indexOf(obrazec);
        }


        /***************************************************************************
         * Vrátí nemodifikovatelný seznam všech spravovaných obrázků.
         *
         * @return  Požadovaný seznam
         */
    //%I+ <135
    //    private
    //%I-
    //%A+ >135
        @Override public
    //%A-
        List<IKreslený> seznamKreslených()
        {
            return Collections.unmodifiableList(předměty);
        }

    //%A+ >135

        /***************************************************************************
         * Přihlásí posluchače změny velikosti pole.
         *
         * @param  posluchač  Přihlašovaný posluchač
         * @return  Informace o tom, byl-li polsuchač doopravdy přidán -
         *          false oznamuje, že posluchač už byl přihlášen
         *          a nebylo jej proto třeba přidávat.
         */
        @Override public boolean přihlašPřizpůsobivý(IPřizpůsobivý posluchač)
        {
            return přizpůsobiví.add(posluchač);
        }


        /***************************************************************************
         * Odhlásí posluchače změny velikosti pole.
         *
         * @param  posluchač  Odhlašovaný posluchač
         * @return  Informace o tom, byl-li polsuchač doopravdy odebrán -
         *          false oznamuje, že posluchač už nebyl přihlášen
         *          a nebylo jej proto třeba odebírat.
         */
        @Override public boolean odhlašPřizpůsobivý(IPřizpůsobivý posluchač)
        {
            return přizpůsobiví.remove(posluchač);
        }


        /***************************************************************************
         * Nastaví, zda se mají přihlášeným posluchačům hlásit změny
         * velikosti kroku a vrátí původní nastavení.
         *
         * @param hlásit  Požadované nastavení (true=hlásit, false=nehlasit).
         * @return Původní nastavení
         */
        @Override public boolean hlásitZměnyRozměru(boolean hlásit)
        {
            boolean ret = hlásitZměnyRozměru;
            hlásitZměnyRozměru = hlásit;
            return ret;
        }

    //%A-

        /***************************************************************************
         * Přihlásí posluchače událostí klávesnice.
         *
         * @param posluchač  Přihlašovaný posluchač
         */
    //%I+ <135
    //    private
    //%I-
    //%A+ >135
        @Override public
    //%A-
        void přihlašKlávesnici(KeyListener posluchač)
        {
            okno.addKeyListener(posluchač);
        }


        /***************************************************************************
         * Odhlásí posluchače klávesnice.
         *
         * @param posluchač  Odhlašovaný posluchač
         */
    //%I+ <135
    //    private
    //%I-
    //%A+ >135
        @Override public
    //%A-
        void odhlašKlávesnici(KeyListener posluchač)
        {
            okno.removeKeyListener(posluchač);
        }


        /***************************************************************************
         * Přihlásí posluchače událostí myši.
         *
         * @param posluchač  Přihlašovaný posluchač
         */
    //%I+ <135
    //    private
    //%I-
    //%A+ >135
        @Override public
    //%A-
        void přihlašMyš(MouseListener posluchač)
        {
            okno.addMouseListener(posluchač);
        }


        /***************************************************************************
         * Odhlásí posluchače myši.
         *
         * @param posluchač  Odhlašovaný posluchač
         */
    //%I+ <135
    //    private
    //%I-
    //%A+ >135
        @Override public
    //%A-
        void odhlašMyš(MouseListener posluchač)
        {
            okno.removeMouseListener(posluchač);
        }


        /***************************************************************************
         * Uloží obraz aktivního plátna do zadaného souboru.
         *
         * @param soubor Soubor, do nějž se má obraz plátna uložit
         */
    //%I+ <135
    //    private
    //%I-
    //%A+ >135
        @Override public
    //%A-
        void uložJakoObrázek(File soubor)
        {
            BufferedImage bim = getBufferedImage();
            try {
                ImageIO.write(bim, "PNG", soubor);
            } catch(IOException exc)  {
                throw new RuntimeException(
                    "\nObraz aktivního plátna se nepodařilo uložit do souboru " +
                    soubor,  exc);
            }
        }


    //== SOUKROMÉ A POMOCNÉ METODY TŘÍDY ===========================================
    //== SOUKROMÉ A POMOCNÉ METODY INSTANCÍ ========================================

        /***************************************************************************
         * Vrátí obrázek na aktivním plátně.
         * @return Obsah plátna jako obrázek
         */
        private BufferedImage getBufferedImage()
        {
            if (obrazPlátna instanceof BufferedImage) {
                return (BufferedImage) obrazPlátna;         //==========>
            }
            else {
                return getBufferedImage(0, 0, šířkaBodů, výškaBodů);
            }
        }


        /***************************************************************************
         * Vrátí obrázek výřezu na aktivním plátně.
         * @return Výřez obsahu plátna jako obrázek
         */
        private BufferedImage getBufferedImage(int x, int y, int šířka, int výška)
        {
            BufferedImage ret = new BufferedImage(šířka, výška,
                                                   BufferedImage.TYPE_INT_ARGB);
            Graphics2D g2d = (Graphics2D)ret.getGraphics();
            g2d.drawImage(obrazPlátna, -x, -y, Kreslítko.OBRAZOR);
            return ret;
        }


        /***************************************************************************
         * Inicializuje některé parametry z konfiguračního souboru.
         * Tento soubor je umístěn v domovském adresáři uživatele
         * ve složce {@code .rup} v souboru {@code bluej.properties}.
         */
        private void konfiguraceZeSouboru() {
            Properties sysProp = System.getProperties();
            String     userDir = sysProp.getProperty("user.home");
            File       rupFile = new File(userDir, ".rup/bluej.properties");
            Properties rupProp = new Properties();
            try {
                Reader reader = new FileReader(rupFile);
                rupProp.load(reader);
                reader.close();
                String sx = rupProp.getProperty("canvas.x");
                String sy = rupProp.getProperty("canvas.y");
                int x = Integer.parseInt(rupProp.getProperty("canvas.x"));
                int y = Integer.parseInt(rupProp.getProperty("canvas.y"));
                pozice = new Point(x, y);
            }catch(Exception e)  {
                pozice = new Point(0, 0);
            }
            okno.setLocation(pozice);
        }


        /***************************************************************************
         * Připraví čáry vyznačující jednotlivá pole aktivního plátna.
         * Pokud se čáry kreslit nemají, vyprázdní odkazy na ně.
         */
        private void připravČáry()
        {
            if (mřížka  &&  (krok > 1))
            {
                if ((svislá == null)  ||  (svislá.length != sloupců)) {
                    svislá = new Čára[sloupců];
                }
                if ((vodorovná == null)  ||  (vodorovná.length != řádků)) {
                    vodorovná = new Čára[řádků];
                }
                for (int i=0, x=krok;   i < sloupců;      i++, x+=krok) {
                    svislá[i] = new Čára(x, 0, x, výškaBodů, barvaČar);
                }
                for (int i=0, y=krok;   i < řádků;   i++, y+=krok) {
                    vodorovná[i] = new Čára(0, y, šířkaBodů, y, barvaČar);
                }
            }
            else
            {
                //Uvolnění doposud používaných instancí
                svislá    = null;
                vodorovná = null;
                mřížka    = false;
            }
        }



    //== VNOŘENÉ A VNITŘNÍ TŘÍDY ===================================================
    //== TESTY A METODA MAIN =======================================================
    //%A+ >135

        private static void w(){try{
            Thread.sleep(10);}catch(InterruptedException e){}
        }

        /***************************************************************************
         * Testovaci metoda
         */
        public static void test() {
            SP.přidej(new Obdélník());                    w();
            SP.přidej(new Elipsa(), new Trojúhelník());   w();
            IO.zpráva("Výchozí obrázek - budu vyjímat výřez");
            Obrázek obr = SP.getObrázek(50, 0, 75, 75);   w();
            SP.přidej(obr);                               w();
            SP.setBarvaPozadí(Barva.ČERNÁ);               w();
            IO.zpráva("Obrázek přidaný?");
            obr.setPozice(100, 50);                       w();
            IO.zpráva("Posunutý?");
            obr.setRozměr(150, 150);                        w();
            IO.zpráva("Zvětšený?");
            SP.setKrokRozměr(50, 5, 2);
    //        SP.setKrokRozměr(1, 50, 50);
    //        SP.uložJakoObrázek(new File("D:/SMAZAT.PNG"));

            System.exit(0);
        }


        /***************************************************************************
         * Testovaci metoda
         */
        public static void test2()
        {
            IO.zpráva("Hotovo");
            SP.přidej(new Obdélník   (0, 0, 300, 300));
            SP.přidej(new Elipsa     (0, 0, 300, 300));
            SP.přidej(new Trojúhelník(0, 0, 300, 300));
            IO.zpráva("Nakresleno");

            System.exit(0);
        }
        /** @param args Paremtry příkazového řádku - nepoužité  */
        public static void main(String[] args) { test2(); } /**/
    //%A-
    }


///#############################################################################
///#############################################################################
///#############################################################################

    /***************************************************************************
     * Rozhraní {@code IPlátno} deklaruje kompletní sadu metod
     * implementovaných třídou {@code Plátno}.
     * Představuje společného rodiče výkonné a testovací třídy,
     * tj. třídy {@code PlátnoBěžné} a třídy {@code PlátnoTestovací}.
     */
    private static class SprávceTestovací implements ISprávce
    {
    //== KONSTANTNÍ ATRIBUTY TŘÍDY =================================================
    //== PROMĚNNÉ ATRIBUTY TŘÍDY ===================================================
    //== STATICKÝ INICIALIZAČNÍ BLOK - STATICKÝ KONSTRUKTOR ========================
    //== KONSTANTNÍ ATRIBUTY INSTANCÍ ==============================================
    //== PROMĚNNÉ ATRIBUTY INSTANCÍ ================================================
    //== PŘÍSTUPOVÉ METODY VLASTNOSTÍ TŘÍDY ========================================
    //== OSTATNÍ NESOUKROMÉ METODY TŘÍDY ===========================================

    //##############################################################################
    //== KONSTRUKTORY A TOVÁRNÍ METODY =============================================

        SprávceTestovací()
        {
        }

    //== ABSTRAKTNÍ METODY =========================================================
    //== PŘÍSTUPOVÉ METODY VLASTNOSTÍ INSTANCÍ =====================================

    @Override public void setKrokRozměr(int krok, int pŠířka, int pVýška){
            přidejVoláníMetody("setKrokRozměr");
        }
    @Override public void setPozice(int x, int y) {
            přidejVoláníMetody("setPozice");
        }
    @Override public int getKrok() {
        přidejVoláníMetody("setPozice");
        return 0;
    }
    @Override public int getSloupců() {
        přidejVoláníMetody("setPozice");
        return 0;
    }
    @Override public int getŘádků() {
        přidejVoláníMetody("setPozice");
        return 0;
    }
    @Override public boolean isMřížka() {
        přidejVoláníMetody("setPozice");
        return false;
    }
    @Override public void setMřížka(boolean zobrazit) {
        přidejVoláníMetody("setPozice");
    }
    @Override public Barva getBarvaPozadí() {
        přidejVoláníMetody("setPozice");
        return null;
    }
    @Override public void setBarvaPozadí(Barva barva) {
        přidejVoláníMetody("setPozice");
    }
    @Override public String getNázev() {
        přidejVoláníMetody("setPozice");
        return null;
    }
    @Override public void setNázev(String název) {
        přidejVoláníMetody("setPozice");
    }
//%A+ >135
    @Override public void setKrokRozměr(int krok, int pŠířka, int pVýška, Object měnič) {
        přidejVoláníMetody("setPozice");
    }
    @Override public void setKrok(int velikost) {
        přidejVoláníMetody("setPozice");
    }
    @Override public int getBšířka() {
        přidejVoláníMetody("setPozice");
        return 0;
    }
    @Override public int getBVýška() {
        přidejVoláníMetody("setPozice");
        return 0;
    }
    @Override public Rozměr getRozměr() {
        přidejVoláníMetody("setPozice");
        return null;
    }
    @Override public void setRozměr(int sloupců, int řádků) {
        přidejVoláníMetody("setPozice");
    }
    @Override public void setRozměr(Rozměr rozměr) {
        přidejVoláníMetody("setPozice");
    }
    @Override public boolean isViditelné() {
        přidejVoláníMetody("setPozice");
        return false;
    }
    @Override public void setViditelné(boolean viditelné) {
        přidejVoláníMetody("setPozice");
    }
    @Override public int getNekresli() {
        přidejVoláníMetody("setPozice");
        return 0;
    }
    @Override public Pozice getPozice() {
        přidejVoláníMetody("setPozice");
        return null;
    }
    @Override public void setPozice(Pozice pozice) {
        přidejVoláníMetody("setPozice");
    }
    @Override public Obrázek getObrázek(int x, int y, int šířka, int výška) {
        přidejVoláníMetody("setPozice");
        return null;
    }
//%A-



    //== OSTATNÍ NESOUKROMÉ METODY INSTANCÍ ========================================

    @Override public void překresli() {
        přidejVoláníMetody("setPozice");
    }
    @Override public void nekresli() {
        přidejVoláníMetody("setPozice");
    }
    @Override public void vraťKresli() {
        přidejVoláníMetody("setPozice");
    }
    @Override public boolean odstraň(IKreslený obrazec) {
        přidejVoláníMetody("setPozice");
        return false;
    }
    @Override public void odstraňVše() {
        přidejVoláníMetody("setPozice");
    }
    @Override public int přidej(IKreslený... obrazec) {
        přidejVoláníMetody("setPozice");
        return 0;
    }
    @Override public boolean přidejNad(IKreslený současný, IKreslený přidaný) {
        přidejVoláníMetody("setPozice");
        return false;
    }
    @Override public boolean přidejPod(IKreslený současný, IKreslený přidaný) {
        přidejVoláníMetody("setPozice");
        return false;
    }
//%A+ >135
    @Override public void vypniHodiny() {
        přidejVoláníMetody("setPozice");
    }
    @Override public void zapniHodiny() {
        přidejVoláníMetody("setPozice");
    }
    @Override public boolean přidejNavrch(IKreslený přidaný) {
        přidejVoláníMetody("setPozice");
        return false;
    }
    @Override public boolean přidejDospod(IKreslený přidaný) {
        přidejVoláníMetody("setPozice");
        return false;
    }
    @Override public int pořadí(IKreslený obrazec) {
        přidejVoláníMetody("setPozice");
        return 0;
    }
    @Override public List<IKreslený> seznamKreslených() {
        přidejVoláníMetody("setPozice");
        return null;
    }
    @Override public boolean přihlašPřizpůsobivý(IPřizpůsobivý posluchač) {
        přidejVoláníMetody("setPozice");
        return false;
    }
    @Override public boolean odhlašPřizpůsobivý(IPřizpůsobivý posluchač) {
        přidejVoláníMetody("setPozice");
        return false;
    }
    @Override public boolean hlásitZměnyRozměru(boolean hlásit) {
        přidejVoláníMetody("setPozice");
        return false;
    }
    @Override public void přihlašKlávesnici(KeyListener posluchač) {
        přidejVoláníMetody("setPozice");
    }
    @Override public void odhlašKlávesnici(KeyListener posluchač) {
        přidejVoláníMetody("setPozice");
    }
    @Override public void přihlašMyš(MouseListener posluchač) {
        přidejVoláníMetody("setPozice");
    }
    @Override public void odhlašMyš(MouseListener posluchač) {
        přidejVoláníMetody("setPozice");
    }
    @Override public void uložJakoObrázek(File soubor) {
        přidejVoláníMetody("setPozice");
    }
//%A-



    //== SOUKROMÉ A POMOCNÉ METODY TŘÍDY ===========================================

        /***************************************************************************
         * Zaznamená volání metody do systémových proměnných. Systémové proměnné
         * začánají na {@code rup.io.method.} a následuje označení metody
         * ve tvaru {@code jménoMetody_typParametru1_typParametru2}
         *
         * @param metoda   Označení metody
         */
        private static void přidejVoláníMetody( String metoda )
        {
            String kód = "rup.io.method." + metoda;
            String hodnota = System.getProperty(kód, "0");
            int počet = Integer.valueOf(hodnota);
            počet++;
            System.setProperty(kód, "" + počet);
        }

    //== SOUKROMÉ A POMOCNÉ METODY INSTANCÍ ========================================
    //== VNOŘENÉ A VNITŘNÍ TŘÍDY ===================================================
    //== TESTOVACÍ TŘÍDY A METODY ==================================================
    }


///#############################################################################
///#############################################################################
///#############################################################################

//== TESTY A METODA MAIN =======================================================
//%A+ >135

    private static void w(){try{
        Thread.sleep(10);}catch(InterruptedException e){}
    }

    /***************************************************************************
     * Testovaci metoda
     */
    public static void test() {
        SP.přidej(new Obdélník());                    w();
        SP.přidej(new Elipsa(), new Trojúhelník());   w();
        IO.zpráva("Výchozí obrázek - budu vyjímat výřez");
        Obrázek obr = SP.getObrázek(50, 0, 75, 75);   w();
        SP.přidej(obr);                               w();
        SP.setBarvaPozadí(Barva.ČERNÁ);               w();
        IO.zpráva("Obrázek přidaný?");
        obr.setPozice(100, 50);                       w();
        IO.zpráva("Posunutý?");
        obr.setRozměr(150, 150);                        w();
        IO.zpráva("Zvětšený?");
        SP.setKrokRozměr(50, 5, 2);
//        SP.setKrokRozměr(1, 50, 50);
//        SP.uložJakoObrázek(new File("D:/SMAZAT.PNG"));

        System.exit(0);
    }


    /***************************************************************************
     * Testovaci metoda
     */
    public static void test2()
    {
        IO.zpráva("Hotovo");
        SP.přidej(new Obdélník   (0, 0, 300, 300));
        SP.přidej(new Elipsa     (0, 0, 300, 300));
        SP.přidej(new Trojúhelník(0, 0, 300, 300));
        IO.zpráva("Nakresleno");

        System.exit(0);
    }
    /** @param args Paremtry příkazového řádku - nepoužité  */
    public static void main(String[] args) { test2(); } /**/
//%A-
}


//%%-  ----- Začátek ignorovaného závěrečného textu -----
////////////////////////////////////////////////////////////////////////////////
//== TESTY A METODA MAIN =======================================================

    /***************************************************************************
     * Testovací metoda.
     */
    public static void test()
    {
        IO.zpráva("Až se pokocháš, stiskni OK");
        System.exit(0);
    }
    /** @param args Parametry příkazového řádku - nepoužívané. */
    public static void main( String[] args )  {  test();  }
}
